#ifndef UTF8_STRING_H
#define UTF8_STRING_H

#include <stdio.h>
#include <stdint.h>

#include <vector>

namespace utf8util {
    static bool is_continuation_byte(char c) {
        return (c & 0xC0) == 0x80;
    }

    // return the number of bits in c that is set.
    static int bitcount(char c) {
        unsigned char uc = c;
        uc = (uc & 0x55) + ((uc >> 1) & 0x55);
        uc = (uc & 0x33) + ((uc >> 2) & 0x33);
        uc = (uc & 0xF) + ((uc >> 4) & 0xF);
        return uc & 0xF;
    }

    // return how many byte it should lead
    // -1 means it's not a leading byte.
    static int leading_byte(char c) {
        if (is_continuation_byte(c))
            return -1;

        // reset all the x in `1110xxxx`
        signed char sc = c;
        sc &= sc >> 1;
        sc &= sc >> 2;
        sc &= sc >> 4;
        c = sc;
        int ans = bitcount(c);
        if (ans == 0)
            return 1;
        else
            return ans;
    }
};

#define UTF8_CHARLEN 4
union utf8_char {
    uint32_t m_whole;
    char m_part[UTF8_CHARLEN];

    bool operator == (const utf8_char& other) const {
        return m_whole == other.m_whole;
    };

    void print_to(FILE* file) const {
        fputc(m_part[0], file);
        int j = 1;
        while (utf8util::is_continuation_byte(m_part[j]) && j < 4) {
            fputc(m_part[j], file);
            j++;
        }
    }
};

const utf8_char UTF8_CHARPAD = {
    .m_whole = 0xFFFFFFFF
};

/**
 * string class with utf8 support. i.e
 * it will treat a utf8 sequence as a character
 * Design:
 *   a vector<utf8_char> as underlay storage
 **/
class utf8_string {
public:
    utf8_string() {

    }
    /**
     * create a new utf8_string from bytes sequence.
     * return how many utf8 characters in `str'
     **/
    int from_bytes(const char* str);
    /**
     * return the number of characters (not bytes) in the string;
     ***/
    int length() const;
    /**
     * wrote the n-th utf8 character to where `output' points to.
     * index starts with 0. return 1 when succeses.
     **/
    utf8_char at(unsigned int n) const;
    /**
     * return a combined string from self and `str' to `output'
     **/
    utf8_string concat(const utf8_string& str) const;
    /**
     * return the position( in utf8 sense) of a substring
     **/
    int find(const utf8_string& pattern) const;
    /**
     * replace all occurrences of `pattern' with `to'.
     * return times of replacement.
     **/
    int replace(const utf8_string& pattern, const utf8_string& to);

    void output(FILE *file) const {
        for (int i = 0; i < m_chars.size(); i++) {
            m_chars[i].print_to(file);
        }
    }

private:
    // Underlay byte sequence
    std::vector<utf8_char> m_chars;


};

#endif
